#pragma once

#include "MetaTable.h"
#include <gtest/gtest.h>

namespace metatableTest
{
	// Declare base class
	class MyBase
	{
		mtMETA_DEC;
	public:
		int state;
		MyBase() : state(0) {}

		void update()
		{
			state++;
		}

		Signal <void()> eventFast;
	};

	class SlotClass
	{
		mtMETA_DEC;
	public:
		int state;
		SlotClass() : state(0) {}
		void fast()
		{
			state = 1;
		}
	};

	class MyClass : public MyBase
	{
		mtMETA_DEC;
	public: 
		void add(int value)
		{
			state += value;
		}

		void reset()
		{
			state = 0;
		}

		void draw()
		{
			int a = 4;
		}
	};

	class MyClass1 : public MyBase
	{
		mtMETA_DEC;
	public:
		void update()
		{
			int a = 4;
		}
	};

	class Record
	{
		mtMETA_DEC;
	public:
		int		number;
		float	cost;
	};
	// implement meta-tables

	mtBEGIN(Record)
		mtCONSTRUCTOR(void);
		mtVARIABLE(number);
		mtVARIABLE(cost);
	mtEND

	mtBEGIN(MyBase)
		mtMETHOD(update);
		mtSIGNAL(eventFast, fast);
	mtEND


	mtBEGIN(MyClass)
		mtBASE(MyBase);
		mtMETHOD(reset);
		mtMETHOD(add);
	mtEND


	mtBEGIN(MyClass1)
		mtBASE(MyBase);
		mtMETHOD(update);
	mtEND

	mtBEGIN(SlotClass)
		mtMETHOD(fast);
	mtEND
}

TEST(MetaTable, setVariableValue)
{
	auto r = new metatableTest::Record();
	r->number = 0;
	r->cost = 0.0f;
	auto t = r->getMetaTable();

	mtSET_VARIABLE(r, number, 1);
	mtSET_VARIABLE(r, cost, 8.0f);

	EXPECT_EQ(true, r->number == 1);
	EXPECT_EQ(true, r->cost == 8.0f);
}

TEST(MetaTable, isSubclass)
{
	auto c1 = new metatableTest::MyClass1();
	auto c2 = new metatableTest::MyClass();
	auto t = c1->getMetaTable();
	EXPECT_EQ(true, c1->getMetaTable()->isSubclass<metatableTest::MyBase>());
	EXPECT_EQ(true, c2->getMetaTable()->isSubclass<metatableTest::MyBase>());
	EXPECT_EQ(false, c1->getMetaTable()->isSubclass<metatableTest::MyClass>());
}

TEST(MetaTable, callMethod)
{
	auto c = new metatableTest::MyClass();

	mtCALL(c, add, 5);
	EXPECT_EQ(5, c->state);
	mtCALL(c, reset, 12);
	EXPECT_EQ(5, c->state);
	mtCALL(c, reset);
	EXPECT_EQ(0, c->state);
}

TEST(MetaTable, callBaseClassMethod)
{
	auto c = new metatableTest::MyClass();
	mtCALL(c, update);
	EXPECT_EQ(1, c->state);
}

TEST(MetaTable, ConnectToSlot)
{
	metatableTest::MyBase base;
	metatableTest::SlotClass s;

	MetaClass* table1 = base.getMetaTable();
	MetaClass* table2 = s.getMetaTable();
	MetaFunction* foo = mtGET_METHOD(table2, fast, void());//table2->getMethod(getStaticStringHash("fast"), getTypeID<void()>(), true);
	table1->signals[0]->connect(&base, &s, foo);

	EXPECT_EQ(0, s.state);
	base.eventFast();
	EXPECT_EQ(1, s.state);
}
